/**
 * @file nivel2.c
 * @brief Implementación de las funciones del modulo Nivel 2
 *
 * Fichero que contiene el cuerpo de las funciones definidas por la biblioteca Nivel 2
 */

#include "nivel2.h"

#define SABM 0x2F											/*!< Campo de control de SABM*/
#define DISC 0x4F											/*!< Campo de control de UA*/
#define UA 0x67												/*!< Campo de control de DISC*/
#define I_MASK 0x00											/*!< Mascara de campo de control de I*/
#define RR_MASK 0x01										/*!< Mascara de campo de control de RR*/

#define I(s,r) (((r & 0x07) << 5) | ((s & 0x07)<< 1))		/*!< Creacion del campo de control de I*/
#define RR(r) (((r & 0x07) << 5) | 0x01)					/*!< Creacion del campo de control de RR*/
#define R(Control) ((Control >> 5) & 0x07)					/*!< Recuperacion de N_R a partir de campo de control*/
#define S(Control) ((Control >> 1) & 0x07)					/*!< Recuperacion de N_S a partir de campo de control*/

#define L1a_TYPE 1											/*!< Tipo de mensaje de nivel 1a utilizado por el nivel 2*/
#define SABM_SIZE 2											/*!< Tamanno de un mensaje SABM*/
#define UA_SIZE 2											/*!< Tamanno de un mensaje UA*/
#define DISC_SIZE 2											/*!< Tamanno de un mensaje DISC*/
#define RR_SIZE 2											/*!< Tamanno de la cabecera de un mensaje RR*/
#define I_SIZE 2											/*!< Tamanno de la cabecera de un mensaje I*/

typedef enum {NO_CONECTADO, SOLICITAR_CONEXION, ACEPTAR_CONEXION,  CONEXION, SOLICITAR_DESCONEXION} LEVEL2_STATE;		/*!< Estado de la biblioteca de Nivel 2*/
typedef enum {I_TYPE, RR_TYPE, SABM_TYPE, UA_TYPE, DISC_TYPE, UNDEF} DATAGRAM_TYPE;										/*!< Tipo de datagrama recibido*/


/*********** CONSTANTES DE BIBLIOTECA ***********************/

typedef struct _l2Constants{
	BYTE SABMDatagram[SABM_SIZE];			/*!< Datagrama SABM*/
	BYTE UADatagram[UA_SIZE];				/*!< Datagrama UA*/
	BYTE DISCDatagram[DISC_SIZE];			/*!< Datagrama DISC*/
	BYTE IDatagram[I_SIZE];					/*!< Mascara de cabecera del datagrama I*/
	BYTE RRDatagram[RR_SIZE];				/*!< Mascara de cabecera del datagrama RR*/
	BYTE broadcastMAC[ETH_ALEN];			/*!< Direccion MAC de difusion*/
} L2_CONSTANTS;

const L2_CONSTANTS l2Const={{0x00, SABM},{0x00, UA},{0x00, DISC},{0x00, I_MASK},{0x00, RR_MASK},{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};		/*!< Creacion de la estructura de constantes*/


/*********** VARIABLES GLOBALES DE BIBLIOTECA ***************/

/*Variables globales persistentes entre conexiones*/
short l2Inicialized=0;										/* Estado de inicializacion del Nivel 2*/
LEVEL2_STATE l2State=NO_CONECTADO;							/* Estado del Nivel 2*/
tpfNotificacionDesdeNivel2 notifyFromLevel2=NULL;			/* Funcion de notificacion desde el Nivel 2*/

/*Variables globales de conexion*/
BYTE remoteDir[ETH_ALEN];									/* Direccion remota de conexion*/
uint32_t N_S=0;												/* Contador N_S -> Contador de mensaje enviado*/
uint32_t N_R=0;												/* Contador N_R -> Contador de mensaje recibido*/
int burstIndex=0;											/* Indice de la rafaga de envio*/
/*int lastConfirmed=0;										Ultimo mensaje confirmado*/
DATAGRAM_BUFFER dataBuffer=NULL;							/* Buffer circular de datagramas*/


/*********** FUNCIONES PRIVADAS DE BIBLIOTECA ***************/

/**
* @brief Funcion de recepcion de datos en bruto desde el Nivel 1a.
* Vuelca el datagrama en data y categoriza el tipo de datagrama en datagramType.
*
* Nota: Funcion bloqueante. Bloquea al proceso a la espera de un nuevo datagrama hasta,
* a los sumo, timeout milisegundos.
*
* @param data Datagrama recibido
* @param timeout Timeout de espera en la recepcion (En milisegundos).
* @param datagramType Puntero a DATAGRAM_TYPE donde se volcara el tipo de
* mensaje recibido.
* @return ETH_ERROR si error, ETH_TIMEOUT si se excedio el tiempo de espera,
* tamanno de mensaje (> 0) si correcto.
*/
int receiveData(BYTE *data, int timeout, DATAGRAM_TYPE *datagramType);

/**
* @brief Inicializa las variables globales de la libreria.
*
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int iniGlobalVariables();

/**
* @brief Limpia las variables globales de la libreria.
*
* Nota: Esta operacion no libera los recursos requisados. Para desalocar
* la memoria reservada, invoquese a freeGlobalVariables.
* @return ETH_OK si correcto, ETH_ERR si error.
*/
void cleanGlobalVariables();

/**
* @brief Libera las variables globales de la libreria.
*
* @return ETH_OK si correcto, ETH_ERR si error.
*/
void freeGlobalVariables();

/**
* @brief Solicita una conexion a DirDst.
*
* @param DirDst Direccion de destino de la solicitud.
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int connectionRequest(BYTE *DirDst);

/**
* @brief Acepta una conexion de DirDst durante timeout milisegundos.
* Vuelca la direccion aceptante de dirAccept.
*
* @param dirDst Direccion MAC de origen de la solicitud.
* @param dirAccept Direccion MAC aceptante.
* @param timeout Tiempo maximo de espera de solicitud.
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int connectionAccept(BYTE *dirDst, BYTE *dirAccept, int timeout);

/**
* @brief Ejecuta el protocolo HDLC a la espera de confirmacion de los
* datagramas enviados, ejecutando las retransmisiones oportunas.
*
* Proporciona un servicio de entrega fiable para el Nivel de enlace.
*
* @return ETH_OK si correcto, ETH_ERROR si error.
*/
int confirmDatagram();

/**
* @brief Retransmite los datagramas no confirmados, en virtud del protocolo
* HDLC.
*
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int retransmision();

/**
* @brief Procesa un datagrama entrante de tipo I, en virtud del protocolo
* HDLC.
*
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int processIDatagram();

/**
* @brief Procesa un datagrama entrante de tipo DISC, en virtud del protocolo
* HDLC.
*
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int processDiscDatagram();

/**
* @brief Procesa un datagrama entrante de tipo RR, en virtud del protocolo
* HDLC.
*
* @param msg Datagrama RR entrante al sistema.
* @return ETH_OK si correcto, ETH_ERR si error.
*/
int processRRDatagram(BYTE *msg);

/**
* @brief Crea un datagrama de tipo I a partir de los parametros entrantes,
* y vuelca el tamanno del datagrama en datagramSize.
*
* Nota: Esta funcion reserva memoria para el datagrama creado. Para liberarla,
* llamese a free.
*
* @param data Mensaje a insertar en el datagrama de tipo I.
* @param dataSize Tamanno del mensaje a insertar.
* @param datagramSize Puntero a entero en el que se volcara el tamanno final
* del datagrama.
* @return Datagrama de tipo I creado, NULL si error.
*/
BYTE *createIDatagram(BYTE *data, int dataSize, int *datagramSize);

/**
* @brief Crea un datagrama de tipo RR a partir de los parametros entrantes.
*
* @param datagram Zona de memoria sobre la que crear el datagrama de tipo RR.
* @param N_R_counter Contador N_R que se desea incluir en el mensaje.
*/
void createRRDatagram(BYTE *datagram, int N_R_counter);

/**
* @brief Retorna el tipo del datagrama.
*
* @param datagram Datagrama a analizar.
* @return Tipo del datagrama parametro, UNDEF si irreconocible.
*/
DATAGRAM_TYPE getType(const BYTE *datagram);

/**
* @brief Retorna el valor del contador N_R del datagrama datagram.
*
* @param datagram Datagrama a analizar.
* @return N_R del mensaje.
*/
int getN_R(const BYTE *datagram);

/**
* @brief Retorna el valor del contador N_S del datagrama datagram.
*
* @param datagram Datagrama a analizar.
* @return N_S del mensaje.
*/
int getN_S(const BYTE *datagram);

/**
* @brief Funcion de notificacion desde el Nivel 1a.
*
* Controla el flujo de mensajes entrantes desde el Nivel 1a hacia
* el Nivel 2, aceptandolos o rechazandolos pertinentemente.
*
* @param Direccion_Remitente Direccion MAC del remitente del mensaje.
* @param Tamano Tamanno, en byte's, del mensaje entrante.
* @param Mensaje Cuerpo del mensaje.
* @param Tipo Tipo del mensaje recibido.
* @param time Fecha de recepcion del mensaje.
* @return ETH_OK si la trama debe ser aceptada, ETH_ERROR si la trama debe ser rechazada.
*/
int notifyFromLevel1a(const BYTE *Direccion_Remitente , int Tamano, const BYTE *Mensaje, int Tipo, struct timeval * time);


#define cpyEthDir(dst, src) (memcpy((dst), (src), ETH_ALEN))						/*!< Copia la direccion MAC src en la direccion MAC dst*/
#define cmpEthDir(dirA, dirB) (memcmp((dirA), (dirB), ETH_ALEN))					/*!< Compara direcciones MAC*/
#define isBroadcastEthDir(dir) (cmpEthDir((dir), l2Const.broadcastMAC)==0 ? 1: 0)	/*!< Retorna 1 si la direccion MAC es de broadcast, 0 si no*/


int notifyFromLevel1a(const BYTE *Direccion_Remitente , int Tamano, const BYTE *Mensaje, int Tipo, struct timeval * time)
{
	DATAGRAM_TYPE datagramType;
	int msg_N_R, msg_N_S;
	BYTE RRDatagram[RR_SIZE];
	uint32_t msgL2Size=0;
	
	/*CdE de coherencia de llamada*/
	if(Mensaje==NULL || Tipo != L1a_TYPE){
		return ETH_ERROR;
	}
	
	datagramType=getType(Mensaje);					/*Calculo del tipo del mensaje*/
	if(datagramType==UNDEF){						/*Tipo desconocido -> Mensaje no perteneciente al protocolo HDLC*/
		return ETH_ERROR;
	}
	
	
	/*Seleccion de procesamiento del mensaje en funcion del tipo del datagrama y el estado del Nivel 2*/
	
	switch(l2State){
		case SOLICITAR_CONEXION:											/*---> SOLICITAR CONEXION <---*/
		case SOLICITAR_DESCONEXION:											/*---> SOLICITAR DESCONEXION <---*/
			if(cmpEthDir(Direccion_Remitente, remoteDir)!=0){										/*CdE de coherencia de direccion entrante*/
				return ETH_ERROR;
			}
			
			switch(datagramType){	
				case UA_TYPE:					/* UA */
					return ETH_OK;
				default:						/* TIPO INESPERADO */				
					return ETH_ERROR;
			}
			
			break;
			
			
		case ACEPTAR_CONEXION:												/*---> ACEPTAR CONEXION <---*/
			if(!isBroadcastEthDir(remoteDir) && cmpEthDir(remoteDir, Direccion_Remitente)!=0){		/*CdE de coherencia de direccion entrante*/
				return ETH_ERROR;
			}
			
			switch(datagramType){
				case SABM_TYPE:					/* SABM */							
					return ETH_OK;
				default:						/* TIPO INESPERADO */	
					return ETH_ERROR;
			}
			
			break;
		
		
		case CONEXION:														/*---> CONEXION <---*/
			if(cmpEthDir(Direccion_Remitente, remoteDir)!=0){										/*CdE de coherencia de direccion entrante*/
				return ETH_ERROR;
			}
		

			switch(datagramType){
				case I_TYPE:					/* I */
					msg_N_S=getN_S(Mensaje);
					msg_N_R=getN_R(Mensaje);
					
					if(N_R!=msg_N_S){	/*Mensaje I con contador N_S desfasado -> Error del enlace en el envio de RR previo -> Reenvio de RR adecuado*/
					
						/*Reenvio de RR*/
						createRRDatagram(RRDatagram, (msg_N_S+1)%(BANDWIDTH+1));		
						EnviarDatagramaNivel1a(Direccion_Remitente, RR_SIZE, RRDatagram, L1a_TYPE);
						
						return ETH_ERROR;
						
					} else{				/*Mensaje I correcto*/
					
						if(notifyFromLevel2==NULL){		/*No se ha proporcionado funcion de notificacion -> Datagrama correcto*/
							return ETH_OK;
						}
						
						/*Extraccion del tamanno del mensaje de nivel superior (Sin cabeceras HDLC y sin relleno expureo del Nivel 1a)*/
						((BYTE *)(&msgL2Size))[3]=Mensaje[0];
						msgL2Size=ntohl(msgL2Size);	
	
						return notifyFromLevel2(msgL2Size, Mensaje+HDLC_HEADER_SIZE);				/*Llamada a la funcion de notificacion de Nivel 2*/
					}
					
					break;
					
				case DISC_TYPE:					/* DISC*/
					if(notifyFromLevel2==NULL){		/*No se ha proporcionado funcion de notificacion -> Datagrama correcto*/
						return ETH_OK;
					}
						
					/*Extraccion del tamanno del mensaje de nivel superior (Sin cabeceras HDLC y sin relleno expureo del Nivel 1a)*/
					((BYTE *)(&msgL2Size))[3]=Mensaje[0];
					msgL2Size=ntohl(msgL2Size);	
	
					return notifyFromLevel2(msgL2Size, Mensaje+HDLC_HEADER_SIZE);				/*Llamada a la funcion de notificacion de Nivel 2*/
				
				case RR_TYPE:					/* RR*/
					msg_N_R=getN_R(Mensaje);
					if(msg_N_R!=((N_S+1)%(BANDWIDTH+1))){			/*Mensaje RR con contador N_R desfasado -> Error en la transmision -> Descarte de datagrama (Ya ha sido procesado)*/	
						return ETH_ERROR;
					}
					
					return ETH_OK;
					
				case SABM_TYPE:					/* SABM*/		
					/*Mensaje SABM desfasado -> El enlace remoto no ha recibido confirmacion UA de conexion -> Reenvio de datagrama UA de confirmacion*/
					EnviarDatagramaNivel1a(Direccion_Remitente, UA_SIZE, l2Const.UADatagram, L1a_TYPE);
					
					return ETH_ERROR;
					
				default:						/* TIPO INESPERADO*/					
					return ETH_ERROR;	
			}
			
			break;
		
		
		case NO_CONECTADO:												/*---> NO CONECTADO <---*/
			switch(datagramType){
				case DISC_TYPE:					/* DISC*/	
					if(cmpEthDir(Direccion_Remitente, remoteDir)!=0){								/*CdE de coherencia de direccion entrante*/
						return ETH_ERROR;
					}
					
					/*Mensaje DISC desfasado -> El enlace remoto no ha recibido confirmacion UA de desconexion -> Reenvio de datagrama UA de confirmacion*/
					EnviarDatagramaNivel1a(Direccion_Remitente, UA_SIZE, l2Const.UADatagram, L1a_TYPE);
			
					return ETH_ERROR;
				
				default:						/* TIPO INESPERADO*/	
					return ETH_ERROR;
					
			}
			
			break;
			
		
		default:													/*---> ESTADO INESPERADO <---*/	
			return ETH_ERROR;
	}
			
	return ETH_OK;
}

int IniciarNivel2(tpfNotificacionDesdeNivel2 notifyFunction)
{	
	if(l2Inicialized){				/*Nivel 2 ya iniciado*/
		return ETH_ERROR;
	}
	
	/*Inicializacion del Nivel 1a*/
	if(InicializarNivel1a(1, 0, notifyFromLevel1a, N2_T_INTERRUPCION)!=ETH_OK){
		return ETH_ERROR;
	}

	/*Inicializacion de variables globales de biblioteca*/
	if(iniGlobalVariables()!=ETH_OK){
		FinalizarNivel1a();
		return ETH_ERROR;
	}
	
	l2Inicialized=1;
	l2State=NO_CONECTADO;
	notifyFromLevel2=notifyFunction;

	return ETH_OK;
}

int FinalizarNivel2()
{
	if(!l2Inicialized){				/*Nivel 2 no iniciado*/
		return ETH_ERROR;
	}
	
	/*Finalizacion del Nivel 1a*/
	if(FinalizarNivel1a()!=ETH_OK){
		return ETH_ERROR;
	}
	
	/*Liberacion de variables globales de biblioteca*/
	freeGlobalVariables();
	l2Inicialized=0;
	l2State=NO_CONECTADO;
	notifyFromLevel2=NULL;

	return ETH_OK;
}


int SolicitarConexion(BYTE *DireccionEthernetDestino)
{
	int nIntentos=0;
	int reqState=ETH_ERROR;

	if(!l2Inicialized || DireccionEthernetDestino==NULL || l2State!=NO_CONECTADO){		/*CdE*/
		return ETH_ERROR;
	}
	
	/*Limpieza de variables globales de biblioteca*/
	cleanGlobalVariables();
	
	l2State=SOLICITAR_CONEXION;
	cpyEthDir(remoteDir, DireccionEthernetDestino);

	/*Bucle de solicitud remota de conexion*/
	nIntentos=0;
	reqState=0;
	do{
		
		nIntentos++;
		reqState=connectionRequest(DireccionEthernetDestino);
	}while(reqState!=ETH_OK && nIntentos<N2_MAX_REINT);


	if(reqState!=ETH_OK){			/*Imposible conectar -> Reintentos agotados -> Intento de conexion fallido*/
		l2State=NO_CONECTADO;
		return ETH_ERROR;
	}
	
	l2State=CONEXION;

	return ETH_OK;
}

int AceptarConexion(BYTE *DireccionEthernetDestino, int timeout)
{	
	BYTE dirAceptante[ETH_ALEN];
	
	if(!l2Inicialized || DireccionEthernetDestino==NULL || l2State!=NO_CONECTADO){		/*CdE*/
		return ETH_ERROR;
	}

	/*Limpieza de variables globales de biblioteca*/
	cleanGlobalVariables();
	
	l2State=ACEPTAR_CONEXION;
	cpyEthDir(remoteDir, DireccionEthernetDestino);
	
	if(connectionAccept(DireccionEthernetDestino, dirAceptante, timeout)!=ETH_OK){
		l2State=NO_CONECTADO;
		return ETH_ERROR;
	}
	
	if(isBroadcastEthDir(DireccionEthernetDestino)){					/*Si se acepta en difusion -> Volcado de la direccion aceptante*/
		cpyEthDir(remoteDir, dirAceptante);
	}
	else if(cmpEthDir(DireccionEthernetDestino, dirAceptante)!=0){		/*Respondio un host inesperado*/
		l2State=NO_CONECTADO;
		return ETH_ERROR;
	}
	
	l2State=CONEXION;
	
	return ETH_OK;
}

#define FREE_ALL {if(IDatagram!=NULL){free(IDatagram);}}
int EnviarDatos(BYTE *data, int size)
{
	int datagramSize=0;
	BYTE *IDatagram=NULL;
	
	if(!l2Inicialized || size>N2_MAX_DATA_SIZE || l2State!=CONEXION || data==NULL){		/*CdE*/
		return ETH_ERROR;
	}
	
	/*Creacion de datagrama I de envio*/
	IDatagram=createIDatagram(data, size, &datagramSize);
	if(IDatagram==NULL || datagramSize<0){
		return ETH_ERROR;
	}
	
	/*Inserccion del datagrama I creado en el buffer circular de datagramas, a la espera de confirmacion remota*/
	if(insertDatagramBuffer(dataBuffer, IDatagram, datagramSize)!=OK){
		FREE_ALL;
		return ETH_ERROR;
	}
	
	/*Envio del datagrama*/
	if(EnviarDatagramaNivel1a(remoteDir, datagramSize, IDatagram, L1a_TYPE)!=ETH_OK){
		undo(dataBuffer);
		FREE_ALL;
		return ETH_ERROR;
	}
	
	burstIndex++;
	if(burstIndex>=BANDWIDTH){						/*Finalizada la rafaga de envio -> Se ha alcanzado el tamanno maximo de ventana de emision*/
		burstIndex=0;
		if(confirmDatagram()!=ETH_OK){				/*Solicitud de confirmacion de datagramas enviados*/
			cleanDatagramBuffer(dataBuffer);
			l2State=NO_CONECTADO;
			FREE_ALL;
			return ETH_ERROR;
		}
		
	}
	
	N_S=(N_S+1) % (BANDWIDTH+1);					/*Incremento del contador N_S mod(BANDWIDTH+1) -> Permite distinguir entre buffer vacio y lleno*/

	FREE_ALL;	

	return ETH_OK;
}
#undef FREE_ALL

int RecibirDatos(BYTE *data, int timeout)
{
	int size;
	DATAGRAM_TYPE datagramType;
	
		
	if(!l2Inicialized || l2State!=CONEXION || data==NULL){			/*CdE*/
		return ETH_ERROR;
	}
	
	/*Recepcion de datos*/
	size=receiveData(data, timeout, &datagramType);
	
	/*Calculo del retorno en funcion del tipo de datagrama*/
	switch(datagramType){
		case I_TYPE:						/* I*/
			return size;
		case DISC_TYPE:						/* DISC*/
			return 0;
		default:							/* TIMEOUT o TIPO INESPERADO*/
			if(size == ETH_TIMEOUT) {
			  return ETH_TIMEOUT;
			}
			
			return ETH_ERROR;
	}	
}
	
	
int receiveData(BYTE *data, int timeout, DATAGRAM_TYPE *datagramType)
{
	int state=ETH_OK;
	BYTE msg[N1A_MAX_FRAME];
	BYTE msgDir[ETH_ALEN];
	uint32_t msgL2Size=0;
	int msgType;
	struct timeval timestamp;
	int size;
	
	if(!(l2State==CONEXION || l2State==SOLICITAR_DESCONEXION) || datagramType==NULL){		/*CdE*/
		return ETH_ERROR;
	}
	
	*datagramType=UNDEF;
	
	/*Bloqueo del proceso a la espera de datagramas entrantes*/
	do{	
		state=EsperaNuevoDatagrama(timeout);
	}while(state!=ETH_TIMEOUT && state!=ETH_OK);


	/*Salida por expiracion de tiempo de espera (TIMEOUT)*/
	if(state==ETH_TIMEOUT){	
		return ETH_TIMEOUT;
	}
	
	
	/*Recepcion de datos desde el Nivel 1a*/
	size=RecibirDatagramaNivel1a(msgDir, msg, &msgType, &timestamp);
	if(size<=0){
		return ETH_ERROR;
	}
	
	
	/*Calculo del tamanno efectivo del mensaje -> Primer byte de la cabecera HDLC*/
	((BYTE *)(&msgL2Size))[3]=msg[0];
	msgL2Size=ntohl(msgL2Size);			/*Conversion big endian-little endian para adaptarlo a la codificacion del host*/
	
	
	*datagramType=getType(msg);			/*Extraccion del tipo HDLC del datagrama*/
	switch(*datagramType){
		case I_TYPE:					/* I*/
			if(data==NULL || processIDatagram()!=ETH_OK){
				return ETH_ERROR;
			}

			memcpy(data, msg+HDLC_HEADER_SIZE, msgL2Size);			/* Volcado de datos al buffer parametro de entrada*/
				
			break;
			
		case DISC_TYPE:					/* DISC*/
			if(processDiscDatagram()!=ETH_OK){						/* Procesamiento de DISC -> Inicio de desconexion*/
				return ETH_ERROR;
			}
			
			break;
		
		case RR_TYPE:					/* RR*/
			if(processRRDatagram(msg)!=ETH_OK){						/* Procesamiento de RR -> Confirmacion de datagramas*/
				return ETH_ERROR;
			}
		
			break;


		case UA_TYPE:					/* UA*/									
			break;
						
		default:						/* TIPO INESPERADO*/
			return ETH_ERROR;
	}
	
	return msgL2Size;
}


int Desconectar()
{
	int state=ETH_OK, i=0;
	DATAGRAM_TYPE datagramType;
	
	if(!l2Inicialized || l2State!=CONEXION){			/*CdE*/
		return ETH_ERROR;
	}
	
	l2State=SOLICITAR_DESCONEXION;
	
	do{
		i++;
		
		/*Envio de solicitud de desconexion*/
		if(EnviarDatagramaNivel1a(remoteDir, DISC_SIZE, l2Const.DISCDatagram, L1a_TYPE)!=ETH_OK){
			l2State=CONEXION;
			return ETH_ERROR;
		}
	
		/*Recepcion de solicitud de desconexion*/
		state=receiveData(NULL, N2_TMAX_CONFIRM, &datagramType);

		if(state==ETH_ERROR){		
			return ETH_ERROR;
		}
	}while((state==ETH_TIMEOUT || !(state==ETH_OK && datagramType==UA_TYPE)) && i<N2_MAX_REINT);
	
	l2State=NO_CONECTADO;

	if(i>=N2_MAX_REINT && state!=ETH_OK){		/*Imposible desconectar -> Reintentos agotados -> Fallo de desconexion*/
		return ETH_ERROR;
	}
	
	return ETH_OK;
}


int confirmDatagram()
{
	int state=ETH_OK;
	int i=1;
	DATAGRAM_TYPE datagramType;
	
	
	do{
		state=receiveData(NULL, N2_TMAX_CONFIRM, &datagramType);
	}while(datagramType!=RR_TYPE && state!=ETH_TIMEOUT && state!=ETH_ERROR);
	
	
	/*Retransmisiones sucesivas hasta vaciar el buffer de mensajes sin confirmacion*/
	while((!isEmptyDatagramBuffer(dataBuffer)|| state==ETH_TIMEOUT) && i<N2_MAX_REINT)
	{
		if(retransmision()!=ETH_OK){											/*Retransmision de los datagramas I sin confirmacion*/
			return ETH_ERROR;
		}
		
		do{
			state=receiveData(NULL, N2_TMAX_CONFIRM, &datagramType);			/*Recepcion de RR de confirmacion*/
		} while(!(state==ETH_OK && datagramType==RR_TYPE) && state!=ETH_TIMEOUT && state!=ETH_ERROR);
		
		if(state==ETH_ERROR){
			return ETH_ERROR;
		}
		
		i++;
	}
	
	if(i>=N2_MAX_REINT && state!=ETH_OK){		/*Imposible confirmar -> Reintentos agotados -> Fallo de confirmacion*/
		return ETH_ERROR;
	}
	
	return ETH_OK;
}


int retransmision()
{
	MSG_DATA msgRetrans=NULL;
	ITERATION_STATE state;
	
	/*Creacion de una EdD soporte para la retransmision*/
	msgRetrans=iniMsgData();
	if(msgRetrans==NULL){
		return ETH_ERROR;
	}
	
	restartIteratorDatagramBuffer(dataBuffer);	/*Reinicio de iterador del buffer de datagramas*/
	
	/*Iteracion y reenvio de los datagramas no confirmados*/
	state=iterateDatagramBuffer(dataBuffer, msgRetrans);
	while(state!=ITERATION_END && state!=ITERATION_ERR){	
		if(EnviarDatagramaNivel1a(remoteDir, getSizeMsgData(msgRetrans), getMsgMsgData(msgRetrans), L1a_TYPE)!=ETH_OK){
			freeMsgData(msgRetrans);
			return ETH_ERROR;
		}
		
		cleanMsgData(msgRetrans);
		state=iterateDatagramBuffer(dataBuffer, msgRetrans);
	}
	
	freeMsgData(msgRetrans);
	if(state==ITERATION_ERR){			/*Fallo de la iteracion*/
		return ETH_ERROR;
	}
	
	return ETH_OK;
}

int connectionRequest(BYTE *DirDst)
{
	BYTE UADatagramReceived[N1A_MIN_FRAME];
	BYTE UADir[ETH_ALEN];
	struct timeval timestamp;
	int receivedType;
	int HDLCType;
	
	if(DirDst==NULL){					/*CdE*/
		return ETH_ERROR;
	}
	
	/*Envio de SABM de solicitud de conexion*/
	if(EnviarDatagramaNivel1a(DirDst, SABM_SIZE, l2Const.SABMDatagram, L1a_TYPE)!=ETH_OK){
		return ETH_ERROR;
	}
	
	/*Espera de UA de confirmacion de conexion*/
	if(EsperaNuevoDatagrama(N2_TMAX_CONFIRM)!=ETH_OK){
		return ETH_ERROR;
	}
	
	/*Recepcion de UA de confirmacion de conexion*/
	if(RecibirDatagramaNivel1a(UADir, UADatagramReceived, &receivedType, &timestamp)<=0){
		return ETH_ERROR;
	}
	
	HDLCType=getType(UADatagramReceived);
	if(HDLCType!=UA_TYPE || cmpEthDir(UADir, remoteDir)!=0){
		return ETH_ERROR;
	}
																											
	return ETH_OK;
}	

int connectionAccept(BYTE *dirDst, BYTE *dirAccept, int timeout)
{
	BYTE SABMDatagramReceived[N1A_MIN_FRAME];
	struct timeval timestamp;
	int receivedType;
	int HDLCType;
	
	
	/*Espera de SABM de solicitud de conexion*/
	if(EsperaNuevoDatagrama(timeout)!=ETH_OK){
		return ETH_ERROR;
	}

	/*Recepcion de SABM de solicitud de conexion*/
	if(RecibirDatagramaNivel1a(dirAccept, SABMDatagramReceived, &receivedType, &timestamp)<=0){
		return ETH_ERROR;
	}
	
	HDLCType=getType(SABMDatagramReceived);
	if(HDLCType!=SABM_TYPE || (!isBroadcastEthDir(remoteDir) && cmpEthDir(dirAccept, remoteDir)!=0)){
		return ETH_ERROR;
	}
	
	/*Envio de UA de confirmacion de conexion*/
	if(EnviarDatagramaNivel1a(dirAccept, UA_SIZE, l2Const.UADatagram, L1a_TYPE)!=ETH_OK){
		return ETH_ERROR;
	}
		
	return ETH_OK;
}

int processIDatagram()
{
	BYTE RRDatagram[N1A_MIN_FRAME];
		
	N_R=(N_R+1)%(BANDWIDTH+1);					/*Incremento del contador N_R mod(BANDWIDTH+1) -> Permite distinguir entre buffer vacio y lleno*/


	/*Confirmacion de recepcion -> Creacion y envio de RR de confirmacion*/
	createRRDatagram(RRDatagram, N_R);
	if(EnviarDatagramaNivel1a(remoteDir, RR_SIZE, RRDatagram, L1a_TYPE)!=ETH_OK){
		N_R= N_R==0? BANDWIDTH: N_R-1;
		return ETH_ERROR;
	}

	return ETH_OK;
}

int processDiscDatagram()
{
	l2State=NO_CONECTADO;
	
	/*Envio de UA de confirmacion de desconexion*/
	if(EnviarDatagramaNivel1a(remoteDir, UA_SIZE, l2Const.UADatagram, L1a_TYPE)!=ETH_OK){
		return ETH_ERROR;
	}
	
	return ETH_OK;	
}

int processRRDatagram(BYTE *msg)
{
	int msg_N_R;
	
	if(msg==NULL){
		return ETH_ERROR;
	}
	
	msg_N_R=getN_R(msg);
	
	/*Confirmacion de los mensajes con N_S inferior al recibido en confirmacion*/
	if(cleanIntervalDataDatagramBuffer(dataBuffer, msg_N_R)!=OK){
		return ETH_ERROR;
	}
	
	return ETH_OK;
}

int iniGlobalVariables()
{
	/*Creacion del buffer circular de almacenamiento de datagramas no confirmados*/
	dataBuffer=iniDatagramBuffer(BANDWIDTH);
	if(dataBuffer==NULL){
		return ETH_ERROR;
	}
	
	/*Reinicio de variables globales de la biblioteca*/
	cleanGlobalVariables();
	
	return ETH_OK;
}

void cleanGlobalVariables()
{
	int i;
	
	N_S=0;
	N_R=0;
	burstIndex=0;
	
	/*Borrado de la direccion destino remota*/
	for(i=0; i<ETH_ALEN; i++){
		remoteDir[i]=0;
	}
	
	cleanDatagramBuffer(dataBuffer);
}

void freeGlobalVariables()
{
	/*Reinicio de variables globales de la biblioteca*/
	cleanGlobalVariables();
	
	/*Liberacion del baffer circular de datagramas no confirmados*/
	freeDatagramBuffer(dataBuffer);
	dataBuffer=NULL;
}

BYTE *createIDatagram(BYTE *data, int dataSize, int *datagramSize)
{
	uint32_t bigEndianSize;
	BYTE *datagram=NULL;

	datagram=(BYTE *)calloc(dataSize+I_SIZE, sizeof(BYTE));
	if(datagram==NULL){
		return NULL;
	}

	bigEndianSize=htonl(dataSize);
	
	datagram[0]=((BYTE *)(&bigEndianSize))[3];				/*Campo longitud*/
	datagram[1]=I(N_S,N_R);									/*Campo control*/
	memcpy(datagram+HDLC_HEADER_SIZE, data, dataSize);		/*Copia del mensaje*/

	
	*datagramSize=dataSize+HDLC_HEADER_SIZE;				/*TamannoFinal = TamannoDatos + TamannoCabecera*/
	
	return datagram;
}

void createRRDatagram(BYTE *datagram, int N_R_counter)
{
	datagram[0]=0x00;					/*Campo longitud*/
	datagram[1]=RR(N_R_counter);		/*Campo control*/

	return;	
}

DATAGRAM_TYPE getType(const BYTE *datagram)
{
	BYTE controlField;
	
	controlField=datagram[1];		/*Extraccion del campo de control*/
	if(controlField==l2Const.SABMDatagram[1]){			/* SABM*/
		return SABM_TYPE;
	}else if(controlField==l2Const.UADatagram[1]){		/* UA*/
		return UA_TYPE;
	}else if(controlField==l2Const.DISCDatagram[1]){	/* DISC*/
		return DISC_TYPE;
	}

	/*Aplicacion sucesiva de enmarcaramiento de bits para comparacion con mascaras I y RR*/

	controlField=controlField & 0x1F;			/*Enmascaramiento de N_R*/
	if(controlField==l2Const.RRDatagram[1]){			/* RR*/
		return RR_TYPE;
	}

	controlField=controlField & 0xF1;			/*Enmascaramiento de N_S*/
	if(controlField==l2Const.IDatagram[1]){				/* I*/
		return I_TYPE;
	}

	return UNDEF;
}


int getN_R(const BYTE *datagram)
{
	return R(datagram[1]);
}

int getN_S(const BYTE *datagram)
{
	return S(datagram[1]);
}

